package vn.edu.fpt.xml.back.services.news;

import com.sun.org.apache.xerces.internal.dom.AttributeMap;
import org.apache.commons.lang3.StringUtils;
import org.apache.velocity.VelocityContext;
import org.apache.velocity.app.Velocity;
import org.apache.velocity.app.VelocityEngine;
import org.htmlcleaner.CleanerProperties;
import org.htmlcleaner.DomSerializer;
import org.htmlcleaner.HtmlCleaner;
import org.htmlcleaner.TagNode;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Component;
import org.springframework.web.client.RestTemplate;
import org.w3c.dom.Document;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;
import vn.edu.fpt.xml.back.config.XnConfiguration;
import vn.edu.fpt.xml.back.dtos.news.ArticleEntities;
import vn.edu.fpt.xml.back.dtos.news.Item;
import vn.edu.fpt.xml.back.dtos.news.Rss;
import vn.edu.fpt.xml.back.entities.ArticleEntity;
import vn.edu.fpt.xml.back.reader.exceptions.ReadException;
import vn.edu.fpt.xml.back.reader.news.RssReader;

import javax.annotation.Resource;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpressionException;
import javax.xml.xpath.XPathFactory;
import java.io.*;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * Created by hiephn on 2014/04/24.
 */
@Component
public class NewsReaderService {

    protected static final Logger LOGGER = LoggerFactory.getLogger(NewsReaderService.class);

    @Resource(name = "rssReader")
    protected RssReader rssReader;
    @Resource(name = "restTemplate")
    protected RestTemplate restTemplate;
    @Resource(name = "xnConfiguration")
    protected XnConfiguration xnConfiguration;
    protected HtmlCleaner htmlCleaner = new HtmlCleaner();
    protected DomSerializer domSerializer = new DomSerializer(new CleanerProperties(), false);
    protected XPath xPath = XPathFactory.newInstance().newXPath();
    protected Map<String, String> channelKeyTitle;

    public void init() {
        Velocity.init();
        channelKeyTitle = new HashMap<String, String>();
        channelKeyTitle.put("thethao", "Thể thao");
        channelKeyTitle.put("giaitri", "Giải trí");
        channelKeyTitle.put("phap-luat", "Pháp luật");
        channelKeyTitle.put("giao-duc", "Giáo dục");
        channelKeyTitle.put("doisong", "Đời sống");
        channelKeyTitle.put("dulich", "Du lịch");
        channelKeyTitle.put("khoa-hoc", "Khoa học");
        channelKeyTitle.put("sohoa", "Số hoá");
        channelKeyTitle.put("oto-xe-may", "Xe");
        channelKeyTitle.put("cong-dong", "Cộng đồng");
        channelKeyTitle.put("tam-su", "Tâm sự");
        channelKeyTitle.put("cuoi", "Cười");
        channelKeyTitle.put("thoi-su", "Thời sự");
        channelKeyTitle.put("goc-nhin", "Góc nhìn");
        channelKeyTitle.put("the-gioi", "Thế giới");
        channelKeyTitle.put("kinhdoanh", "Kinh doanh");
    }

    public void processService() throws ReadException, ParseException, IOException {
        Rss rss = rssReader.readResource("http://vnexpress.net/rss/tin-moi-nhat.rss");
        ArticleEntities articleEntities = toArticleEntities(rss);
        ResponseEntity<String> stringResponseEntity = restTemplate.postForEntity(xnConfiguration.getXnCloudBackEngineArticlePosterUrl(), articleEntities, String.class);

//        rss = rssReader.readResource("http://vnexpress.net/rss/doi-song.rss");
//        articleEntities = toArticleEntities(rss);
//        stringResponseEntity = restTemplate.postForEntity(xnConfiguration.getXnCloudBackEngineArticlePosterUrl(), articleEntities, String.class);
//        rss = rssReader.readResource("http://vnexpress.net/rss/the-gioi.rss");
//        articleEntities = toArticleEntities(rss);
//        stringResponseEntity = restTemplate.postForEntity(xnConfiguration.getXnCloudBackEngineArticlePosterUrl(), articleEntities, String.class);
//        rss = rssReader.readResource("http://vnexpress.net/rss/kinh-doanh.rss");
//        articleEntities = toArticleEntities(rss);
//        stringResponseEntity = restTemplate.postForEntity(xnConfiguration.getXnCloudBackEngineArticlePosterUrl(), articleEntities, String.class);
//        rss = rssReader.readResource("http://vnexpress.net/rss/giai-tri.rss");
//        articleEntities = toArticleEntities(rss);
//        stringResponseEntity = restTemplate.postForEntity(xnConfiguration.getXnCloudBackEngineArticlePosterUrl(), articleEntities, String.class);
//        rss = rssReader.readResource("http://vnexpress.net/rss/the-thao.rss");
//        articleEntities = toArticleEntities(rss);
//        stringResponseEntity = restTemplate.postForEntity(xnConfiguration.getXnCloudBackEngineArticlePosterUrl(), articleEntities, String.class);
//        rss = rssReader.readResource("http://vnexpress.net/rss/phap-luat.rss");
//        articleEntities = toArticleEntities(rss);
//        stringResponseEntity = restTemplate.postForEntity(xnConfiguration.getXnCloudBackEngineArticlePosterUrl(), articleEntities, String.class);
//        rss = rssReader.readResource("http://vnexpress.net/rss/du-lich.rss");
//        articleEntities = toArticleEntities(rss);
//        stringResponseEntity = restTemplate.postForEntity(xnConfiguration.getXnCloudBackEngineArticlePosterUrl(), articleEntities, String.class);
//        rss = rssReader.readResource("http://vnexpress.net/rss/khoa-hoc.rss");
//        articleEntities = toArticleEntities(rss);
//        stringResponseEntity = restTemplate.postForEntity(xnConfiguration.getXnCloudBackEngineArticlePosterUrl(), articleEntities, String.class);
//        rss = rssReader.readResource("http://vnexpress.net/rss/so-hoa.rss");
//        articleEntities = toArticleEntities(rss);
//        stringResponseEntity = restTemplate.postForEntity(xnConfiguration.getXnCloudBackEngineArticlePosterUrl(), articleEntities, String.class);
//        rss = rssReader.readResource("http://vnexpress.net/rss/oto-xe-may.rss");
//        articleEntities = toArticleEntities(rss);
//        stringResponseEntity = restTemplate.postForEntity(xnConfiguration.getXnCloudBackEngineArticlePosterUrl(), articleEntities, String.class);
//        rss = rssReader.readResource("http://vnexpress.net/rss/cong-dong.rss");
//        articleEntities = toArticleEntities(rss);
//        stringResponseEntity = restTemplate.postForEntity(xnConfiguration.getXnCloudBackEngineArticlePosterUrl(), articleEntities, String.class);
//        rss = rssReader.readResource("http://vnexpress.net/rss/tam-su.rss");
//        articleEntities = toArticleEntities(rss);
//        stringResponseEntity = restTemplate.postForEntity(xnConfiguration.getXnCloudBackEngineArticlePosterUrl(), articleEntities, String.class);
//        rss = rssReader.readResource("http://vnexpress.net/rss/cuoi.rss");
//        articleEntities = toArticleEntities(rss);
//        stringResponseEntity = restTemplate.postForEntity(xnConfiguration.getXnCloudBackEngineArticlePosterUrl(), articleEntities, String.class);
    }

    protected ArticleEntities toArticleEntities(Rss rss) throws ParseException {

        List<ArticleEntity> articleEntityList = new ArrayList<ArticleEntity>(rss.getChannel().getItemList().size());
        ArticleEntity articleEntity;
//        String channelTitle = this.extractChannelTitle(rss.getChannel().getTitle());
        for (Item item : rss.getChannel().getItemList()) {
            if (isNewsArticle(item.getLink())) {
                try {
                    String content = this.extractContent(item.getLink(), item.getTitle(), this.extractDescription(item.getDescription()));
                    if (StringUtils.isNotEmpty(content)) {
                        articleEntity = new ArticleEntity();
                        articleEntity.setContent(content);
                        articleEntity.setTitle(item.getTitle());
                        articleEntity.setDescription(this.extractDescription(item.getDescription()));
                        articleEntity.setPublicDateString(item.getPubDate());
                        articleEntity.setChannelTitle(this.extractChannelTitle(item.getLink()));
                        articleEntity.setOriginUrl(item.getLink());
                        articleEntity.setImage(this.extractImgUrl(item.getDescription()));
                        articleEntity.setOnHomePage(true);
                        articleEntityList.add(articleEntity);
                    }
                } catch (ParserConfigurationException e) {
                    LOGGER.error("Ignore item because engine fails to parse html to get content. Html link: " + item.getLink(), e);
                } catch (XPathExpressionException e) {
                    LOGGER.error("Ignore item because engine fails to evaluate xpath to get content. Html link: " + item.getLink(), e);
                } catch (Exception e) {
                    LOGGER.error("Ignore item because engine unexpectedly fails while processing data. Html link: " + item.getLink(), e);
                }
            }
        }

        ArticleEntities articleEntities = new ArticleEntities();
        articleEntities.setArticleEntityList(articleEntityList);
        return articleEntities;
    }

    protected boolean isNewsArticle(String link) {
        return StringUtils.contains(link, "vnexpress.net/tin-tuc");
    }

    protected String extractImgUrl(String description) {
        return StringUtils.substring(
                description,
                StringUtils.indexOf(description, "src=\"") + "src=\"".length(),
                StringUtils.indexOf(description, ".jpg\"") + ".jpg\"".length() - 1
        );
    }

    protected String extractDescription(String description) {
        return StringUtils.substring(
                description,
                StringUtils.lastIndexOf(description, ">") + 1
        );
    }

    protected String extractContent(String url, String articleTitle, String description) throws ParserConfigurationException, XPathExpressionException {
        ResponseEntity<String> responseEntity = restTemplate.getForEntity(url, String.class);
        TagNode tagNode = htmlCleaner.clean(responseEntity.getBody());
        Document document = domSerializer.createDOM(tagNode);
        NodeList parentNodeList = (NodeList) xPath.evaluate("//*[@class=\"fck_detail width_common\"]", document, XPathConstants.NODESET);
        if (parentNodeList.item(0) != null) {
            List<Map<String, String>> contentElementList = new ArrayList<Map<String, String>>();
            NodeList nodeList = parentNodeList.item(0).getChildNodes();
            for (int i = 0; i < nodeList.getLength(); ++i) {
                Node node = nodeList.item(i);
                if (StringUtils.equals("p", node.getNodeName())) {
                    Map<String, String> paragraph = new HashMap<String, String>();
                    paragraph.put("paragraph", node.getTextContent());
                    contentElementList.add(paragraph);
                } else if (StringUtils.equals("table", node.getNodeName())) {
                    /**
                     * VNExpress image table:
                     *
                     * <table align="center" border="0" cellpadding="3" cellspacing="0" class="tplCaption" style="width: 91.04166666666667%;">
                     *     <tbody>
                     *         <tr><td><img alt="Sự khác nhau giữa học sinh và sinh viên." data-natural-="" src="http://m.f29.img.vnecdn.net/2014/04/29/15-5685-1398754879.jpg" data-width="437" data-pwidth="480"></td></tr>
                     *         <tr><td><p class="Image">Sự khác nhau giữa học sinh và sinh viên.</p></td></tr>
                     *     </tbody>
                     * </table>
                     */
                    AttributeMap attributes = (AttributeMap) node.getAttributes();
                    String tableAttributes = this.extractAllAttributeExceptForClass(attributes);

                    Node imageNode = (Node) xPath.evaluate("tbody/tr[1]/td/img", node, XPathConstants.NODE);
                    attributes = (AttributeMap) imageNode.getAttributes();
                    String imageAttribute = this.extractAllAttributeExceptForClass(attributes);

                    String imageCaption = xPath.evaluate("tbody/tr[2]/td/p/text()", node);
                    String imageSource = xPath.evaluate("tbody/tr[2]/td/p/em/text()", node);
                    if (StringUtils.isNotEmpty(imageSource)) {
                        imageCaption = imageCaption + " " + imageSource;
                    }

                    Map<String, String> tableMap = new HashMap<String, String>();
                    tableMap.put("tableAttributes", tableAttributes);
                    tableMap.put("imgAttributes", imageAttribute);
                    tableMap.put("imgCaption", imageCaption);
                    contentElementList.add(tableMap);
                }
            }

            VelocityContext context = new VelocityContext();
            context.put("title", articleTitle);
            context.put("description", description);
            context.put("contentElementList", contentElementList);

            StringWriter writer = new StringWriter();
            Velocity.evaluate(context, writer, "contentString", xnConfiguration.getXnCloudFrontArticleContentHtml());
            String content = writer.toString();
            return content;
        } else {
            return StringUtils.EMPTY;
        }
    }

//    protected String extractChannelTitle(String title) {
//        return StringUtils.substring(
//                title,
//                0,
//                StringUtils.indexOf(title, " - ")
//        );
//    }

    protected String extractAllAttributeExceptForClass(AttributeMap attributeMap) {
        StringBuilder attributeBuilder = new StringBuilder();
        attributeBuilder.append(' ');
        for (int j = 0; j < attributeMap.getLength(); ++j) {
            if (!StringUtils.equals(attributeMap.item(j).getNodeName(), "class")) {
                attributeBuilder.append(attributeMap.item(j).getNodeName());
                attributeBuilder.append("=\"");
                attributeBuilder.append(attributeMap.item(j).getNodeValue());
                attributeBuilder.append("\" ");
            }
        }

        return attributeBuilder.toString();
    }

    protected String extractChannelTitle(String link) {
        String channelKey = null;

        if (StringUtils.contains(link, ".vnexpress.net/")) {
            channelKey = StringUtils.substring(
                    link,
                    StringUtils.indexOf(link, "//") + 2,
                    StringUtils.indexOf(link, ".")
            );
        } else {
            channelKey = StringUtils.substring(
                    link,
                    StringUtils.indexOf(link, "vnexpress.net/tin-tuc/") + "vnexpress.net/tin-tuc/".length()
            );
            channelKey = StringUtils.substring(
                    channelKey,
                    0,
                    StringUtils.indexOf(channelKey, "/")
            );
        }

        return channelKeyTitle.get(channelKey);
    }

}
